﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.IO;
using System.Web.UI;
using TA.DotNetUtils;
using TA.JSON;
using TA.Turbo.Design;
using TA.Win.Utils.Design;

namespace TA.Turbo.Controls
{
	public partial class Repeater
	{
		#region VARS
		SingleInstanceTemplate _header, _footer;
		MultipleInstanceTemplate _viewItem, _editItem;
		RepeaterRenderedItemsContainer _renderedItemsContainer;
		PagerSettings _pager = new PagerSettings();
		RepeaterValue _value = new RepeaterValue();
		List<string> _dataFieldsToRender = new List<string>();
		List<PropertyPair> _fields = new List<PropertyPair>();

		#endregion
		#region PROPS
		#endregion
		#region METHODS
		#region INIT
		/// <summary>
		/// Init.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);

			this.Init_Templates();
			this.Init_Template_Defaults();

			if (this.LoadEntries.PostScenario != null)
				this.LoadEntries.PostScenario.EnsurePostControl(new ControlReference(this) { Value = false, Properties = "SortDirection,SortExpression" });

			if (!this.get_IsDesignMode())
			{
				this.Init_DataFields();
				this.Init_ShortProps();
			}
		}
		/// <summary>
		/// Initializes all of the templates defined by this control.
		/// </summary>
		protected virtual void Init_Templates()
		{
			this.ViewItem.Init();
			this.EditItem.Init();
			this.Header.Init();
			this.Footer.Init();
		}
		/// <summary>
		/// Initializes the default contents of the templates.
		/// </summary>
		protected virtual void Init_Template_Defaults()
		{
			if (this.Header.Template == null)
			{
				this.Header.Container.Controls.Add(new LiteralControl("<div itemscontainer=\"1\">"));
			}
			if (this.Footer.Template == null)
			{
				this._footer.Container.Controls.Add(new LiteralControl("</div>"));
			}
		}
		/// <summary>
		/// Populates the <see cref="Fields"/> collection with value controls from both <see cref="ViewItem"/> and <see cref="EditItem"/> templates.
		/// </summary>
		protected virtual void Init_DataFields()
		{
			this.ViewItem.Populate(this.Fields, path => path.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries));
			this.EditItem.Populate(this.Fields, path => path.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries));
		}
		/// <summary>
		/// Initializes the <see cref="PropertyPair.JSONProperty"/> property of all <see cref="PropertyPair"/>s contained within this control's <see cref="Fields"/> collection.
		/// </summary>
		protected virtual void Init_ShortProps()
		{
			this.Init_ShortProps_Recursive(this.Fields);
		}
		private void Init_ShortProps_Recursive(IEnumerable<PropertyPair> list)
		{
			HashSet<char> usedProps = new HashSet<char>();
			string lastShortProp = "";

			foreach (PropertyPair pp in list)
			{
				if (string.IsNullOrEmpty(pp.JSONProperty))
				{
					string shortProp = null;
					// try first letter of the property
					char initial = pp.ObjectProperty[0];
					if (!usedProps.Contains(initial))
					{
						usedProps.Add(initial);
						shortProp = initial.ToString();
					}
					else
					{
						// try the other case of the first letter
						char initialInvertCase = char.IsLower(initial) ? char.ToUpper(initial) : char.ToLower(initial);
						if (!usedProps.Contains(initialInvertCase))
						{
							usedProps.Add(initialInvertCase);
							shortProp = initialInvertCase.ToString();
						}
						else
						{
							// then use next short prop
							do
							{
								lastShortProp = lastShortProp.PickNext();
							}
							while (lastShortProp.Length == 1 && usedProps.Contains(lastShortProp[0]));
							shortProp = lastShortProp;
						}
					}
					pp.JSONProperty = shortProp;
				}

				this.Init_ShortProps_Recursive(pp.Children);
			}
		}
		/// <summary>
		/// Registers <see cref="Script.DataTemplate"/>, <see cref="Script.Array"/>, "PagerElement.js",
		/// "PagerSettings.js", "RepeaterValue.js", "RepeaterItem.js", "RepeaterPageButton.js",
		/// "RepeaterPager.js", "Repeater.js".
		/// </summary>
		protected internal override void RegisterIncludes(IncludeCollection includes)
		{
			includes.Add(Script.FieldBinding);

			base.RegisterIncludes(includes);

			includes.Add(Script.Element_PerformRecursive);
			includes.Add(Script.Element_ClearChildNodes);
			includes.Add(Script.Element_AddClass);
			includes.Add(Script.Element_RemoveClass);
			includes.Add(Script.DataTemplate);
			includes.Add(Script.Array_AddRange);
			includes.Add(Script.Number);
			includes.Add(Script.ElementSearch);
			includes.Add(Script.Char);
			includes.Add(Script.Array_Clear);
			includes.Add(Script.Array_First);
			includes.Add(Script.Array_Remove);
			includes.Add(Script.Array_IndexOf);
			includes.Add(Script.Array_AddRange);
			includes.Add(Script.String_IsNullOrEmpty);
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.EntryArgumentEventArgs.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.PagerElement.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.PagerSettings.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterValue.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterItem.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterPageButton.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterPager.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.Repeater.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterCommands.js", typeof(Repeater)));
			includes.Add(new ScriptResourceInclude("TA.Turbo.Res.JS.Repeater.RepeaterDisplay.js", typeof(Repeater)));
		}
		/// <summary>
		/// Creates and returns the container control for the data bound items.
		/// </summary>
		/// <returns></returns>
		protected virtual RepeaterRenderedItemsContainer CreateRenderedItemsContainer()
		{
			return new RepeaterRenderedItemsContainer(this);
		}
		#endregion
		#region RENDER
		/// <summary>
		/// Adds view and edit template attributes.
		/// </summary>
		/// <param name="writer"></param>
		protected override void AddAttributesToRender(HtmlTextWriter writer)
		{
			//ViewItem.AddAttributes(writer);
			//EditItem.AddAttributes(writer);

			//JsonSerializer json = new JsonSerializer();
			//using (StringWriter sw = new StringWriter())
			//{
			//  json.Serialize(sw, Pager);
			//  writer.AddAttribute("Pager", sw.ToString());
			//}

			//writer.AddAttribute("PageIndex", PageIndex.ToString());
			//writer.AddAttribute("SortDirection", ((int)SortDirection).ToString());
			//writer.AddAttribute("SortExpression", SortExpression);
			//writer.AddAttribute("SortingMode", ((int)SortingMode).ToString());
			//writer.AddAttribute("DisplayNewItem", DisplayNewItem.ToString().ToLower());
			//writer.AddAttribute("DisplayEdit", DisplayEdit.ToString().ToLower());

			base.AddAttributesToRender(writer);
		}
		public override void RenderInitScriptInternal(HtmlTextWriter writer, string varName)
		{
			base.RenderInitScriptInternal(writer, varName);

			this.ViewItem.RenderInitScript(writer, varName);
			this.EditItem.RenderInitScript(writer, varName);
			this.RenderInitScriptInternal_Fields(writer, varName);
		}

		private void RenderInitScriptInternal_Fields(HtmlTextWriter writer, string varName)
		{
			writer.WriteScript("{0}.get_Value().SetFields(", varName);
			this.RenderInitScriptInternal_Fields_Recursive(new JsonWriter(writer), this.Fields);
			writer.WriteScript(");");
			//[['A', 'Alpha', []],
		}
		void RenderInitScriptInternal_Fields_Recursive(JsonWriter jw, IEnumerable<PropertyPair> props)
		{
			jw.StartArray();
			foreach (PropertyPair pp in props)
			{
				jw.StartArray();

				jw.Value(pp.ObjectProperty);
				jw.Value(pp.JSONProperty);

				if (pp.Children.Count > 0)
				{
					this.RenderInitScriptInternal_Fields_Recursive(jw, pp.Children);
				}

				jw.EndArray();
			}
			jw.EndArray();
		}
		protected override void RenderContents(HtmlTextWriter writer)
		{
			if (this.get_IsDesignMode())
			{
				writer.Write("Repeater<br/>");
			}

			base.RenderContents(writer);
		}
		#endregion
		#region EVENTS
		/// <summary>
		/// Raises events.
		/// </summary>
		/// <param name="name"></param>
		/// <param name="argument"></param>
		public override void RaiseEvent(string name, string argument)
		{
			using (StringReader sr = new StringReader(argument))
			{
				JsonReader jr = new JsonReader(sr);
				switch (name)
				{
					case "ItemAdded":
					case "ItemUpdated":
					case "ItemDeleted":
						IRepeaterEntries repEntries = (IRepeaterEntries)this.Value.Entries.Clone();
						repEntries.Repeater = this;
						repEntries.Clear();

						repEntries.DeserializeContent(jr, null);

						RepeaterItemEventArgs e2 = new RepeaterItemEventArgs
						{
							Entry = repEntries[0],
						};
						switch (name)
						{
							case "ItemAdded":
								OnItemAdded(e2);
								break;
							case "ItemUpdated":
								OnItemUpdated(e2);
								break;
							case "ItemDeleted":
								OnItemDeleted(e2);
								break;
						}
						break;
					case "LoadEntries":
						RepeaterEntriesEventArgs entriesEA = new RepeaterEntriesEventArgs(this, jr);

						OnLoadEntries(entriesEA);
						this.TPage.ResultMain = entriesEA;
						break;
					case "Command":
						RepeaterCommandEventArgs rce = new RepeaterCommandEventArgs();
						rce.Deserialize(jr);
						OnCommand(rce);
						break;
					default:
						base.RaiseEvent(name, argument);
						break;
				}
			}
		}
		/// <summary>
		/// Raises the Command event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnCommand(RepeaterCommandEventArgs e)
		{
			Command.Raise(this, e);
		}
		/// <summary>
		/// Raises the LoadEntries event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnLoadEntries(RepeaterEntriesEventArgs e)
		{
			LoadEntries.Raise(this, e);
		}
		/// <summary>
		/// Raises the ItemAdded event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemAdded(RepeaterItemEventArgs e)
		{
			ItemAdded.Raise(this, e);
		}
		/// <summary>
		/// Raises the ItemUpdated event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemUpdated(RepeaterItemEventArgs e)
		{
			ItemUpdated.Raise(this, e);
		}
		/// <summary>
		/// Raises the ItemDeleted event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnItemDeleted(RepeaterItemEventArgs e)
		{
			ItemDeleted.Raise(this, e);
		}
		/// <summary>
		/// Raises the Deserialize event.
		/// </summary>
		/// <param name="e"></param>
		protected internal virtual void OnDeserialize(DeserializeEventArgs e)
		{
			if (Deserialize != null)
				Deserialize(this, e);
		}
		/// <summary>
		/// Raises the <see cref="CreateEntry"/> event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnCreateEntry(RepeaterItemEventArgs e)
		{
			if (this.CreateEntry != null)
				this.CreateEntry(this, e);
		}
		/// <summary>
		/// Raises the Serialize event.
		/// </summary>
		/// <param name="e"></param>
		protected internal virtual void OnSerialize(SerializeEventArgs e)
		{
			if (Serialize != null)
				Serialize(this, e);
		}
		/// <summary>
		/// Raises the BindItem event.
		/// </summary>
		/// <param name="e"></param>
		protected internal virtual void OnBindItem(BindEventArgs e)
		{
			BindItem.Raise(this, e);
		}
		#endregion
		#region DATA
		protected internal object GetEmptyEntry()
		{
			RepeaterItemEventArgs itemEA = new RepeaterItemEventArgs();
			this.OnCreateEntry(itemEA);
			if (itemEA.Entry == null)
				throw new InvalidOperationException("Deserialization error: RepeaterItemEventArgs.Entry is null. Please check that an entry creating handler is associated with this control and that the handler sets the Entry property of the event args.");
			return itemEA.Entry;
		}
		#endregion
		#endregion
	}
}